package com.starhub.common.security.filter;

import com.alibaba.fastjson.JSON;
import com.starhub.common.exception.AppException;
import com.starhub.common.exception.ErrorResponse;
import com.starhub.common.security.util.JWTUtils;
import com.starhub.common.sys.user.LoginUser;
import com.starhub.engine.sys.role.dao.SysRoleDao;
import com.starhub.engine.sys.role.entity.SysRole;
import com.starhub.engine.sys.user.entity.SysUser;
import com.starhub.utils.Util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * token过滤器 验证token有效性
 *
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Autowired
    private SysRoleDao roleDao;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        try {
            // 放行登录和注册接口
            String uri = request.getRequestURI();
            // 白名单路径直接放行
            if (Arrays.stream(JWTUtils.WHITELIST).anyMatch(p -> pathMatcher.match(p, uri))) {
                chain.doFilter(request, response);
                return; // 白名单请求不需要后续JWT处理
            }

            // 获取 Token
            String token = request.getHeader("Authorization");
            if (!StringUtils.hasText(token)) {
                // 抛出 Token 为空异常
                throw new AppException(403, "Token 为空");
            }
            if (token.startsWith("Bearer ")) {
                token = token.substring(7).trim();
            }
            // 解析 Token
            Claims claims = JWTUtils.parseJWT(token);
            String loginUserStr = claims.getSubject();
            LoginUser loginUser = JSON.parseObject(loginUserStr, LoginUser.class);

            // 判断用户是否有后端菜单管理权限
            if (uri.startsWith("/engine/starhub/")) {
                SysUser sysUser = loginUser.getUser();
                Long roleId = Util.getLongValue(sysUser.getRoleId());
                SysRole role = roleDao.selectByPrimaryKey(roleId);
                List<String> roleCodes = Arrays.asList("admin", "sysadmin");
                String roleCode = "";
                if (role != null && Util.isNotEmpty(role.getRoleCode())) {
                    roleCode = role.getRoleCode().toLowerCase();
                }
                if (!roleCodes.contains(roleCode)) {
                    throw new AppException(401, "无权限");
                }
            }
            // 设置 Spring Security 上下文
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser,
                    null, null);
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 放行请求
            chain.doFilter(request, response);
        } catch (Exception e) {
            // 记录详细错误日志
            log.error("JWT认证失败 | URI: {} | Method: {} | ClientIP: {}",
                    request.getRequestURI(),
                    request.getMethod(),
                    request.getRemoteAddr()); // 最后一个参数会记录完整堆栈信息
            log.debug("Error: {}",
                    e.getMessage(),
                    e); // 最后一个参数会记录完整堆栈信息
            // 初始化默认错误信息
            int statusCode = HttpStatus.FORBIDDEN.value();
            String errorType = "authentication_failure";
            String errorMessage = "认证失败";

            // 根据异常类型细化错误信息
            if (e instanceof AppException) {
                AppException appEx = (AppException) e;
                statusCode = appEx.getCode();
                errorMessage = appEx.getMessage();
            } else if (e instanceof ExpiredJwtException) {
                statusCode = HttpStatus.UNAUTHORIZED.value();
                errorType = "token_expired";
                errorMessage = "令牌已过期";
            } else if (e instanceof JwtException) {
                errorType = "invalid_token";
                errorMessage = "令牌无效";
            } else {
                statusCode = HttpStatus.INTERNAL_SERVER_ERROR.value();
                errorType = "server_error";
                errorMessage = "系统异常，请稍后重试";
            }

            // 构建统一错误响应
            ErrorResponse errorResponse = new ErrorResponse(
                    statusCode,
                    errorType,
                    errorMessage,
                    request.getRequestURI());

            // 设置响应
            response.setStatus(statusCode);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
            try {
                response.getWriter().write(JSON.toJSONString(errorResponse));
            } catch (IOException ex) {
                log.error("响应写入失败: {}", ex.getMessage());
            }
        }
    }
}